/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package pgentity.pool;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import share.PGException;

/**
 *
 * @author Salm
 * @param <E>
 */
public class EntityNonBlockingCache<E> {
    
    private final LoadingCache<Identifier, E> cache;

    private EntityNonBlockingCache(LoadingCache<Identifier, E> cache) {
        this.cache = cache;
    }
    
    public E get(Object... pars)
    {
        try {
            return cache.get(Identifier.get(pars));
        } catch (ExecutionException ex) {
            PGException.pgThrow(ex, "Cannot create entity");
        }
        
        return null;
    }
    
    public ImmutableMap<Identifier, E> get(Collection<Object[]> pars)
    {
        try {
            Collection<Identifier> identifiers = new ArrayList(pars.size());
            for (Object[] par : pars) {
                identifiers.add(Identifier.get(par));
            }
            
            return (ImmutableMap) cache.getAll(identifiers);
        } catch (ExecutionException ex) {
            PGException.pgThrow(ex, "Cannot create entities");
        }
        
        return null;
    }
    
    public void put(E entity, Object... pars)
    {
        cache.put(Identifier.get(pars), entity);
    }
    
    public void remove(Object... pars)
    {
        cache.invalidate(Identifier.get(pars));
    }
    
    public Set<Identifier> getAvails()
    {
        return (Set) cache.asMap().keySet();
    }
    
    
    public static interface Loader<E>
    {
        E load(Object[] par) throws Exception;
//        Map<CacheParam, E> loadAll(List<CacheParam> pars);
    }
    
    public static interface Loader2<E>
    {
        E load(Identifier id) throws Exception;
        Map<Identifier, E> loadAll(List<Identifier> ids) throws Exception;
    }
    
    public static <E> EntityNonBlockingCache<E> make(Class<E> clazz,
            int cacheSize, int expireInSecs, final Loader<E> loader)
    {
        LoadingCache<Identifier, E> innerCache = CacheBuilder.newBuilder()
                .concurrencyLevel(64)
                .expireAfterAccess(expireInSecs, TimeUnit.SECONDS)
                .maximumSize(cacheSize)
                .build(new CacheLoader<Identifier, E>() {
            @Override
            public E load(Identifier k) throws Exception {
                return loader.load(k.params);
            }
        });
        
        return new EntityNonBlockingCache<E>(innerCache);
    }
    
    public static <E> EntityNonBlockingCache<E> make2(Class<E> clazz,
            int cacheSize, int expireInSecs, final Loader2<E> loader)
    {
        LoadingCache<Identifier, E> innerCache = CacheBuilder.newBuilder()
                .concurrencyLevel(64)
                .expireAfterAccess(expireInSecs, TimeUnit.SECONDS)
                .maximumSize(cacheSize)
                .build(new CacheLoader<Identifier, E>() {
            @Override
            public E load(Identifier k) throws Exception {
                return loader.load(k);
            }

            @Override
            public Map<Identifier, E> loadAll(Iterable<? extends Identifier> keys) throws Exception {
                List<Identifier> ids = Lists.newArrayList(keys);
                return loader.loadAll(ids);
            }
        });
        
        return new EntityNonBlockingCache<E>(innerCache);
    }
}